/**
 ******************************************************************************
 * @file    zb32l03x_hal_qspi.c
 * @author  Application Team
 * @Version V1.0.0
 * @Date    2022/09/10
 * @brief
 ******************************************************************************
 **/

#include "zb32l03x_hal.h"

/** @defgroup QSPI
 * @brief QSPI HAL module driver
 * @{
 */
#if defined(HAL_QSPI_MODULE_ENABLED)

//=============================================================================
//                  Constant Definition
//=============================================================================

#define QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE     0x00000000U                     /*!<Indirect write mode*/
#define QSPI_FUNCTIONAL_MODE_INDIRECT_READ      ((uint32_t)0x1UL << 26U)        /*!<Indirect read mode*/
#define QSPI_FUNCTIONAL_MODE_AUTO_POLLING       ((uint32_t)0x2UL << 26U)        /*!<Automatic polling mode*/
#define QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED      ((uint32_t)0x3UL << 26U)        /*!<Memory-mapped mode*/

//=============================================================================
//                  Macro Definition
//=============================================================================

//=============================================================================
//                  Structure Definition
//=============================================================================

//=============================================================================
//                  Global Data Definition
//=============================================================================

//=============================================================================
//                  Private Function Definition
//=============================================================================
static HAL_StatusTypeDef
_QSPI_DMA_Config(
    HAL_DMA_Handle_TypeDef          *hdma,
    HAL_DMA_Channel_TypeDef         channel_id,
    HAL_DMA_Descriptor_Cfg_TypeDef  *pDescriptorCfg)
{
    HAL_DMA_Xfer_Cfg_TypeDef    transfer_cfg;
    HAL_StatusTypeDef           rval = HAL_OK;

    rval = HAL_DMA_Set_Descriptor(hdma, channel_id, pDescriptorCfg, 0);
    if( rval )      return rval;

    // Start DMA
    transfer_cfg.is_immediate = 0;
    transfer_cfg.mode         = HAL_DMA_MODE_BASIC;
    transfer_cfg.type         = DMA_DESCRIPTOR_TYPE_PRIMARY;
    transfer_cfg.timeout      = -1;
    rval = HAL_DMA_Channel_Sart(hdma, channel_id, &transfer_cfg);
    if( rval )      return rval;

    return rval;
}

/**
 *  @brief  Configure QSPI with functional mode
 *
 *  @param [in] pHQspi              Pointer to a QSPI handle
 *  @param [in] cmd                 Pointer to a command structure, @ref QSPI_CommandTypeDef
 *  @param [in] FunctionalMode      Functional mode to configured
 *                                  This parameter can be one of the following values:
 *                                      @arg QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE: Indirect write mode
 *                                      @arg QSPI_FUNCTIONAL_MODE_INDIRECT_READ: Indirect read mode
 *                                      @arg QSPI_FUNCTIONAL_MODE_AUTO_POLLING: Automatic polling mode
 *                                      @arg QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED: Memory-mapped mode
 *  @return
 *      None
 */
static void
_QSPI_Config(
    QSPI_HandleTypeDef      *pHQspi,
    QSPI_CommandTypeDef     *cmd,
    uint32_t                FunctionalMode)
{
    assert_param(IS_QSPI_FUNCTIONAL_MODE(FunctionalMode));

    if( cmd->DataMode != HAL_QSPI_DATA_NONE &&
        FunctionalMode != QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED )
    {
        //Configure the number of data to read or write
        WRITE_REG(pHQspi->Instance->DLR, (cmd->NbData - 1U));
    }

    if( cmd->InstructionMode != HAL_QSPI_INSTRUCTION_NONE )
    {
        if( cmd->AlternateByteMode != HAL_QSPI_ALTERNATE_BYTES_NONE )
        {
            //Configure alternate bytes value
            WRITE_REG(pHQspi->Instance->ABR, cmd->AlternateBytes);

            if (cmd->AddressMode != HAL_QSPI_ADDRESS_NONE)
            {
                /* Configure instruction, address and alternate bytes ----*/
                WRITE_REG(pHQspi->Instance->CCR,
                          (cmd->SIOOMode | cmd->DataMode |                              \
                           (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |                 \
                           cmd->AlternateBytesSize | cmd->AlternateByteMode |           \
                           cmd->AddressSize | cmd->AddressMode | cmd->InstructionMode | \
                           cmd->Instruction | FunctionalMode));

                if( FunctionalMode != QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED )
                {
                    //Configure address value
                    WRITE_REG(pHQspi->Instance->AR, cmd->Address);
                }
            }
            else
            {
                //Configure instruction and alternate bytes
                WRITE_REG(pHQspi->Instance->CCR,
                          (cmd->SIOOMode | cmd->DataMode |                      \
                           (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |         \
                           cmd->AlternateBytesSize | cmd->AlternateByteMode |   \
                           cmd->AddressMode | cmd->InstructionMode |            \
                           cmd->Instruction | FunctionalMode));
            }
        }
        else
        {
            if( cmd->AddressMode != HAL_QSPI_ADDRESS_NONE )
            {
                //Configure instruction and address
                WRITE_REG(pHQspi->Instance->CCR,
                          (cmd->SIOOMode | cmd->DataMode |                                  \
                           (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |                     \
                           cmd->AlternateByteMode | cmd->AddressSize | cmd->AddressMode |   \
                           cmd->InstructionMode | cmd->Instruction | FunctionalMode));

                if( FunctionalMode != QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED )
                {
                    //Configure address value
                    WRITE_REG(pHQspi->Instance->AR, cmd->Address);
                }
            }
            else
            {
                //Configure instruction
                WRITE_REG(pHQspi->Instance->CCR,
                          (cmd->SIOOMode | cmd->DataMode |                  \
                           (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |     \
                           cmd->AlternateByteMode | cmd->AddressMode |      \
                           cmd->InstructionMode | cmd->Instruction | FunctionalMode));
            }
        }
    }
    else
    {
        if( cmd->AlternateByteMode != HAL_QSPI_ALTERNATE_BYTES_NONE )
        {
            //Configure alternate bytes value
            WRITE_REG(pHQspi->Instance->ABR, cmd->AlternateBytes);

            if( cmd->AddressMode != HAL_QSPI_ADDRESS_NONE )
            {
                //Configure address and alternate bytes
                WRITE_REG(pHQspi->Instance->CCR,
                          (cmd->SIOOMode | cmd->DataMode |                      \
                           (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |         \
                           cmd->AlternateBytesSize | cmd->AlternateByteMode |   \
                           cmd->AddressSize | cmd->AddressMode |                \
                           cmd->InstructionMode | FunctionalMode));

                if( FunctionalMode != QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED )
                {
                    //Configure address value
                    WRITE_REG(pHQspi->Instance->AR, cmd->Address);
                }
            }
            else
            {
                //Configure alternate bytes
                WRITE_REG(pHQspi->Instance->CCR,
                          (cmd->SIOOMode | cmd->DataMode |                      \
                           (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |         \
                           cmd->AlternateBytesSize | cmd->AlternateByteMode |   \
                           cmd->AddressMode | cmd->InstructionMode | FunctionalMode));
            }
        }
        else
        {
            if( cmd->AddressMode != HAL_QSPI_ADDRESS_NONE )
            {
                //Configure address
                WRITE_REG(pHQspi->Instance->CCR,
                          (cmd->SIOOMode | cmd->DataMode |                  \
                           (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |     \
                           cmd->AlternateByteMode | cmd->AddressSize |      \
                           cmd->AddressMode | cmd->InstructionMode | FunctionalMode));

                if( FunctionalMode != QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED )
                {
                    //Configure address value
                    WRITE_REG(pHQspi->Instance->AR, cmd->Address);
                }
            }
            else
            {
                //Command with only data phase
                if( cmd->DataMode != HAL_QSPI_DATA_NONE )
                {
                    //Configure CCR register with all communications parameters
                    WRITE_REG(pHQspi->Instance->CCR,
                              (cmd->SIOOMode | cmd->DataMode |                  \
                               (cmd->DummyCycles << QUADSPI_CCR_DCYC_Pos) |     \
                               cmd->AlternateByteMode | cmd->AddressMode |      \
                               cmd->InstructionMode | FunctionalMode));
                }
            }
        }
    }

    return;
}


/**
 *  @brief  Wait for a flag state until timeout.
 *
 *  @param [in] pHQspi          Pointer to a QSPI handle
 *  @param [in] Flag            The checked flag type
 *  @param [in] Status          The expected flags
 *  @param [in] Timeout         Duration of the timeout (unit:msec)
 *  @return
 *      HAL status
 */
static HAL_StatusTypeDef
_QSPI_WaitFlagUntilTimeout(
    QSPI_HandleTypeDef      *pHQspi,
    uint32_t                Flag,
    FlagStatus              Status,
    uint32_t                Timeout)
{
    uint32_t    Tickstart = HAL_GetTick();

    while( (__HAL_QSPI_GET_FLAG(pHQspi, Flag)) != Status )
    {
        /* Check for the Timeout */
        if( Timeout == HAL_MAX_DELAY ||
            (Timeout && (HAL_GetTick() - Tickstart) < Timeout) )
            continue;

        pHQspi->State      = HAL_QSPI_STATE_ERROR;
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_TIMEOUT;

        return HAL_TIMEOUT;
    }

    return HAL_OK;
}

/**
 *  @brief  Initialize the QSPI MSP.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_MspInit(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);
}


/**
 *  @brief  DeInitialize the QSPI MSP.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);
}

/**
 *  @brief  Transfer Error callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_ErrorCallback(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_QSPI_ErrorCallback could be implemented in the user file
    */
}


/**
 *  @brief  Abort completed callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_AbortCpltCallback(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE: This function should not be modified, when the callback is needed,
            the HAL_QSPI_AbortCpltCallback could be implemented in the user file
    */
}

/**
 *  @brief  Command completed callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_CmdCpltCallback(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE: This function should not be modified, when the callback is needed,
            the HAL_QSPI_CmdCpltCallback could be implemented in the user file
    */
}


/**
 *  @brief  Rx Transfer completed callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_RxCpltCallback(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE: This function should not be modified, when the callback is needed,
            the HAL_QSPI_RxCpltCallback could be implemented in the user file
    */
}

/**
 *  @brief  Tx Transfer completed callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_TxCpltCallback(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE: This function should not be modified, when the callback is needed,
            the HAL_QSPI_TxCpltCallback could be implemented in the user file
    */
}

/**
 *  @brief  FIFO Threshold callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_FifoThresholdCallback(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_QSPI_FIFOThresholdCallback could be implemented in the user file
    */
}


/**
 *  @brief  Status Match callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_StatusMatchCallback(QSPI_HandleTypeDef *pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_QSPI_StatusMatchCallback could be implemented in the user file
    */
}

/**
 *  @brief  Timeout callback.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
__weak void HAL_QSPI_TimeOutCallback(QSPI_HandleTypeDef * pHQspi)
{
    UNUSED(pHQspi);

    /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_QSPI_TimeOutCallback could be implemented in the user file
    */
}

//=============================================================================
//                  Public Function Definition
//=============================================================================

/**
 *  @brief  Initialize the QSPI mode according to the specified parameters
 *              in the QSPI_InitTypeDef and initialize the associated handle.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Init(QSPI_HandleTypeDef *pHQspi)
{
    HAL_StatusTypeDef   rval = HAL_OK;

    assert_param(IS_QSPI_ALL_INSTANCE(pHQspi->Instance));
    assert_param(IS_QSPI_CLOCK_PRESCALER(pHQspi->Init.ClockPrescaler));
    assert_param(IS_QSPI_FIFO_THRESHOLD(pHQspi->Init.FifoThreshold));
    assert_param(IS_QSPI_SSHIFT(pHQspi->Init.SampleShifting));
    assert_param(IS_QSPI_FLASH_SIZE(pHQspi->Init.FlashSize));
    assert_param(IS_QSPI_CS_HIGH_TIME(pHQspi->Init.ChipSelectHighTime));
    assert_param(IS_QSPI_CLOCK_MODE(pHQspi->Init.ClockMode));
    assert_param(IS_QSPI_DUAL_FLASH_MODE(pHQspi->Init.DualFlash));

    if( pHQspi->Init.DualFlash == HAL_QSPI_DUALFLASH_DISABLE )
        assert_param(IS_QSPI_FLASH_ID(pHQspi->Init.FlashBKID));

    if( !pHQspi || pHQspi->Init.ClockPrescaler > 200 )
        return HAL_ERROR;

    if( pHQspi->State == HAL_QSPI_STATE_RESET )
    {
        __HAL_UNLOCK(pHQspi);

        /* Init the low level hardware : GPIO, CLOCK, NVIC */
        HAL_QSPI_MspInit(pHQspi);

        /* Configure the default timeout for the QSPI memory access */
        pHQspi->Timeout = HAL_QSPI_TIMEOUT_DEFAULT_VALUE;
    }

    /* Configure QSPI FIFO Threshold */
    MODIFY_REG(pHQspi->Instance->CR, QUADSPI_CR_FTHRES,
               ((pHQspi->Init.FifoThreshold - 1U) << QUADSPI_CR_FTHRES_Pos));

    /* Wait till BUSY flag reset */
    rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_BUSY, RESET, pHQspi->Timeout);

    if( rval != HAL_OK )
        return rval;

    /* Configure QSPI Clock Pre-scaler,Sample Shift,Flash Size,CS High Time and Clock Mode */
    MODIFY_REG(pHQspi->Instance->CR, (QUADSPI_CR_PRESCALER | QUADSPI_CR_SSHIFT | QUADSPI_CR_FSEL | QUADSPI_CR_DFM),
               ((pHQspi->Init.ClockPrescaler << QUADSPI_CR_PRESCALER_Pos) | \
                pHQspi->Init.SampleShifting | pHQspi->Init.FlashBKID | pHQspi->Init.DualFlash));

    MODIFY_REG(pHQspi->Instance->DCR, (QUADSPI_DCR_FSIZE | QUADSPI_DCR_CSHT | QUADSPI_DCR_CKMODE),
               ((pHQspi->Init.FlashSize << QUADSPI_DCR_FSIZE_Pos) | \
                pHQspi->Init.ChipSelectHighTime | pHQspi->Init.ClockMode));

    __HAL_QSPI_ENABLE(pHQspi);

    pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
    pHQspi->State     = HAL_QSPI_STATE_READY;

    return rval;
}


/**
 *  @brief  De-Initialize the QSPI peripheral.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_DeInit(QSPI_HandleTypeDef *pHQspi)
{
    assert_param(pHQspi);

    if( !pHQspi )
        return HAL_ERROR;

    __HAL_LOCK(pHQspi);

    pHQspi->State = HAL_QSPI_STATE_BUSY;

    /* Disable the QSPI */
    __HAL_QSPI_DISABLE(pHQspi);

    /* DeInit the low level hardware: CLOCK, NVIC... */
    HAL_QSPI_MspDeInit(pHQspi);

    pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
    pHQspi->State     = HAL_QSPI_STATE_RESET;

    __HAL_UNLOCK(pHQspi);

    return HAL_OK;
}


/**
 *  @brief  Set the command configuration.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] cmd         Pointer to a command structure, @ref QSPI_CommandTypeDef
 *  @param [in] Timeout     Timeout duration (unit: msec)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *pHQspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout)
{
    HAL_StatusTypeDef     rval = HAL_OK;

    assert_param(pHQspi);
    assert_param(IS_HAL_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));
    assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
    assert_param(IS_HAL_QSPI_ADDRESS_MODE(cmd->AddressMode));
    assert_param(IS_HAL_QSPI_ADDRESS_SIZE(cmd->AddressSize));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
    assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));
    assert_param(IS_HAL_QSPI_DATA_MODE(cmd->DataMode));

    /* Check the QSPI handle allocation */
    if( !pHQspi || !cmd )
        return HAL_ERROR;

    __HAL_LOCK(pHQspi);

    do {
        if( pHQspi->State != HAL_QSPI_STATE_READY )
        {
            rval = HAL_BUSY;
            break;
        }

        /* Wait till BUSY flag reset */
        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_BUSY, RESET, Timeout);
        if( rval )      break;

        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY;

        /* Call the configuration function */
        _QSPI_Config(pHQspi, cmd, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);

        if( cmd->DataMode != HAL_QSPI_DATA_NONE )
        {
            pHQspi->State = HAL_QSPI_STATE_READY;
        }
        else
        {
            /* wait for the data transfer to complete */
            rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_TC, SET, Timeout);
            if( rval )  break;

            __HAL_QSPI_CLEAR_FLAG(pHQspi, QSPI_FLAG_TC);

            pHQspi->State = HAL_QSPI_STATE_READY;
        }
    } while(0);

    __HAL_UNLOCK(pHQspi);

    return rval;
}


/**
 *  @brief  Handle QSPI interrupt request.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      None
 */
void HAL_QSPI_IRQHandler(QSPI_HandleTypeDef *pHQspi)
{
    __IO uint32_t   *data_reg;
    uint32_t        flags = READ_REG(pHQspi->Instance->SR);
    uint32_t        it_enable = READ_REG(pHQspi->Instance->CR);

    if( (flags & QSPI_FLAG_FT) && (it_enable & QSPI_IT_FT) )        /* FIFO threshold interrupt */
    {
        data_reg = &pHQspi->Instance->DR;

        if( pHQspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_TX )
        {
            while( __HAL_QSPI_GET_FLAG(pHQspi, QSPI_FLAG_FT) == SET )
            {
                uint8_t     *pCur = (uint8_t*)&pHQspi->Cache;
                int         cnt = 0;

                if( pHQspi->TxXferCount >= pHQspi->TxXferSize )
                {
                    CLEAR_BIT(pHQspi->Instance->CR, QSPI_IT_FT);
                    break;
                }

                pHQspi->Cache = 0xFFFFFFFFul;

                cnt = pHQspi->TxXferSize - pHQspi->TxXferCount;
                cnt = (cnt > 3) ? 4 : cnt;

                for(int i = 0; i < cnt; i++)
                    *pCur++ = *pHQspi->pTxBuffPtr++;

                *((__IO uint32_t*)data_reg) = pHQspi->Cache;

                pHQspi->TxXferCount += cnt;
            }
        }
        else if( pHQspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_RX )
        {
            while( __HAL_QSPI_GET_FLAG(pHQspi, QSPI_FLAG_FT) == SET )
            {
                uint8_t     *pCur = (uint8_t*)&pHQspi->Cache;
                int         cnt = 0;

                pHQspi->Cache = *((__IO uint32_t*)data_reg);

                cnt = pHQspi->RxXferSize - pHQspi->RxXferCount;
                cnt = (cnt > 3) ? 4 : cnt;

                for(int i = 0; i < cnt; i++)
                    *pHQspi->pRxBuffPtr++ = *pCur++;

                pHQspi->RxXferCount += cnt;
            }
        }

        HAL_QSPI_FifoThresholdCallback(pHQspi);
    }
    else if( (flags & QSPI_FLAG_TC) && (it_enable & QSPI_IT_TC) )   /* Transfer Complete interrupt */
    {
        /* Clear Complete Flag */
        __HAL_QSPI_CLEAR_FLAG(pHQspi, QSPI_FLAG_TC);

        /* Disable FIFO Threshold, Error, Transfer complete Interrupts */
        CLEAR_BIT(pHQspi->Instance->CR, QSPI_IT_TC | QSPI_IT_TE | QSPI_IT_FT);

        if( pHQspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_TX )
        {
            if( READ_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN) )
            {
                /* Disable the DMA control bit of the QSPI */
                CLEAR_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN);
            }

            pHQspi->State = HAL_QSPI_STATE_READY;

            HAL_QSPI_TxCpltCallback(pHQspi);
        }
        else if( pHQspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_RX )
        {
            if( READ_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN) )
            {
                /* Disable the DMA control bit of the QSPI */
                CLEAR_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN);
            }
            else
            {
                data_reg = &pHQspi->Instance->DR;

                /* Read data until FIFO is empty */
                while( READ_BIT(pHQspi->Instance->SR, QUADSPI_SR_FLEVEL) )
                {
                    uint8_t     *pCur = (uint8_t*)&pHQspi->Cache;
                    int         cnt = 0;

                    pHQspi->Cache = *((__IO uint32_t*)data_reg);

                    cnt = pHQspi->RxXferSize - pHQspi->RxXferCount;
                    cnt = (cnt > 3) ? 4 : cnt;

                    for(int i = 0; i < cnt; i++)
                        *pHQspi->pRxBuffPtr++ = *pCur++;

                    pHQspi->RxXferCount += cnt;
                }
            }

            pHQspi->State = HAL_QSPI_STATE_READY;

            HAL_QSPI_RxCpltCallback(pHQspi);
        }
        else if( pHQspi->State == HAL_QSPI_STATE_BUSY )
        {
            pHQspi->State = HAL_QSPI_STATE_READY;

            HAL_QSPI_CmdCpltCallback(pHQspi);
        }
        else if( pHQspi->State == HAL_QSPI_STATE_ABORT )
        {
            /* Reset functional mode configuration to indirect write mode by default */
            CLEAR_BIT(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE);

            pHQspi->State = HAL_QSPI_STATE_READY;

            if( pHQspi->ErrorCode == HAL_QSPI_ERROR_NONE )
                HAL_QSPI_AbortCpltCallback(pHQspi);
            else
                HAL_QSPI_ErrorCallback(pHQspi);
        }
    }
    else if( READ_BIT(flags, QSPI_FLAG_SM) && READ_BIT(it_enable, QSPI_IT_SM) )     /* Match interrupt */
    {
        /* Clear interrupt */
        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_SM);

        /* Check if the automatic poll mode stop is activated */
        if( READ_BIT(pHQspi->Instance->CR, QUADSPI_CR_APMS) )
        {
            /* Disable Error, Match Interrupts */
            __HAL_QSPI_DISABLE_IT(pHQspi, (QSPI_IT_SM | QSPI_IT_TE));

            pHQspi->State = HAL_QSPI_STATE_READY;
        }

        HAL_QSPI_StatusMatchCallback(pHQspi);
    }
    else if( READ_BIT(flags, QSPI_FLAG_TE) && READ_BIT(it_enable, QSPI_IT_TE) )     /* Transfer Error interrupt */
    {
        /* Clear interrupt */
        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TE);

        /* Disable all Interrupts */
        __HAL_QSPI_DISABLE_IT(pHQspi, QSPI_IT_SM | QSPI_IT_TC | QSPI_IT_TE | QSPI_IT_FT);

        pHQspi->ErrorCode |= HAL_QSPI_ERROR_TRANSFER;

        if( READ_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN) )
        {
            /* Disable the DMA control bit of the QSPI */
            CLEAR_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN);
        }

        pHQspi->State = HAL_QSPI_STATE_READY;

        HAL_QSPI_ErrorCallback(pHQspi);
    }
    else if( READ_BIT(flags, QSPI_FLAG_TO) && READ_BIT(it_enable, QSPI_IT_TO) )     /* Timeout interrupt */
    {
        /* Clear interrupt */
        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TO);

        HAL_QSPI_TimeOutCallback(pHQspi);
    }

    return;
}


/**
 *  @brief  Set the command configuration in interrupt mode.
 *              This function is used only in Indirect Read or Write Modes
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] cmd         Pointer to a command structure, @ref QSPI_CommandTypeDef
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Command_IT(QSPI_HandleTypeDef *pHQspi, QSPI_CommandTypeDef *cmd)
{
    HAL_StatusTypeDef   rval = HAL_OK;

    assert_param(IS_HAL_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));
    assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
    assert_param(IS_HAL_QSPI_ADDRESS_MODE(cmd->AddressMode));
    assert_param(IS_HAL_QSPI_ADDRESS_SIZE(cmd->AddressSize));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
    assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));
    assert_param(IS_HAL_QSPI_DATA_MODE(cmd->DataMode));

    if( !pHQspi || !cmd )
        return HAL_ERROR;

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    do {
        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY;

        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_BUSY, RESET, pHQspi->Timeout);
        if (rval )  break;

        /* Disable Interrupt before configuration */
        if( cmd->DataMode == HAL_QSPI_DATA_NONE )
        {
            WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TE | QSPI_FLAG_TC);
        }

        /* Call the configuration function */
        _QSPI_Config(pHQspi, cmd, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);

        if( cmd->DataMode == HAL_QSPI_DATA_NONE )
        {
            SET_BIT(pHQspi->Instance->CR, QSPI_IT_TE | QSPI_IT_TC);
            break;
        }

        pHQspi->State = HAL_QSPI_STATE_READY;

    } while(0);

    __HAL_UNLOCK(pHQspi);

    return rval;
}


/**
 *  @brief  Transmit an amount of data in blocking mode.
 *              This function is used only in Indirect Write Mode
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] pData       Pointer to data buffer
 *  @param [in] Timeout     Timeout duration (unit: msec)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Transmit(QSPI_HandleTypeDef *pHQspi, uint8_t *pData, uint32_t Timeout)
{
    HAL_StatusTypeDef   rval = HAL_OK;
    __IO uint32_t       *data_reg = &pHQspi->Instance->DR;

    if( !pHQspi || !pData )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
        return HAL_ERROR;
    }

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

   do {
        uint8_t     *pCur = (uint8_t*)&pHQspi->Cache;

        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY_INDIRECT_TX;

        /* Configure the counter and handle size */
        pHQspi->TxXferCount = 0;
        pHQspi->TxXferSize  = READ_REG(pHQspi->Instance->DLR) + 1U;
        pHQspi->pTxBuffPtr  = pData;

        /* The CCR register is configured with the function of indirect write */
        MODIFY_REG(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);

        while( pHQspi->TxXferCount < pHQspi->TxXferSize )
        {
            if( (pHQspi->TxXferCount & 0x3) == 0 )
            {
                if( pHQspi->TxXferCount )
                {
                    /* Wait until FT flag is set to send data */
                    rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_FT, SET, Timeout);
                    if( rval )  break;

                    *((__IO uint32_t*)data_reg) = pHQspi->Cache;
                }

                pHQspi->Cache = 0xFFFFFFFFul;
                pCur = (uint8_t*)&pHQspi->Cache;
            }

            *pCur++ = *pHQspi->pTxBuffPtr++;

            pHQspi->TxXferCount++;
        }

        if( rval )  break;

        /* Wait until FT flag is set to send data */
        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_FT, SET, Timeout);
        if( rval )  break;

        *((__IO uint32_t*)data_reg) = pHQspi->Cache;

        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_TC, SET, Timeout);
        if (rval )  break;

        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TC);

        pHQspi->State = HAL_QSPI_STATE_READY;
    } while(0);

    __HAL_UNLOCK(pHQspi);

    return rval;
}


/**
 *  @brief  Receive an amount of data in blocking mode.
 *              This function is used only in Indirect Read Mode
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] pData       Pointer to data buffer
 *  @param [in] Timeout     Timeout duration (unit: msec)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Receive(QSPI_HandleTypeDef *pHQspi, uint8_t *pData, uint32_t Timeout)
{
    HAL_StatusTypeDef   rval = HAL_OK;
    uint32_t            addr_reg = READ_REG(pHQspi->Instance->AR);
    __IO uint32_t       *data_reg = &pHQspi->Instance->DR;

    if( !pHQspi || !pData )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
        return HAL_ERROR;
    }

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    do {
        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY_INDIRECT_RX;

        /* Configure the counter and handle size */
        pHQspi->RxXferCount = 0;
        pHQspi->RxXferSize  = READ_REG(pHQspi->Instance->DLR) + 1U;
        pHQspi->pRxBuffPtr  = pData;

        /* The CCR register is configured with the function of indirect read */
         MODIFY_REG(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_READ);

        /* Transmission is initiated by rewriting the address in the AR register */
        WRITE_REG(pHQspi->Instance->AR, addr_reg);

        while( pHQspi->RxXferCount < pHQspi->RxXferSize )
        {
            uint8_t     *pCur = (uint8_t*)&pHQspi->Cache;
            int         cnt = 0;

            /* Wait until FT or TC flag is set to read received data */
            rval = _QSPI_WaitFlagUntilTimeout(pHQspi, (QSPI_FLAG_FT | QSPI_FLAG_TC), SET, Timeout);
            if( rval )  break;

            pHQspi->Cache = *((__IO uint32_t*)data_reg);

            cnt = pHQspi->RxXferSize - pHQspi->RxXferCount;
            cnt = (cnt > 3) ? 4 : cnt;

            for(int i = 0; i < cnt; i++)
                *pHQspi->pRxBuffPtr++ = *pCur++;

            pHQspi->RxXferCount += cnt;
        }

        if( rval )  break;

        /* Wait until TC flag is set to go back in idle state */
        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_TC, SET, Timeout);
        if( rval )  break;

        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TC);

        pHQspi->State = HAL_QSPI_STATE_READY;
    } while(0);
    __HAL_UNLOCK(pHQspi);

    return rval;
}

/**
 *  @brief  Send an amount of data in non-blocking mode with interrupt.
 *              This function is used only in Indirect Write Mode
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] pData       Pointer to data buffer
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Transmit_IT(QSPI_HandleTypeDef *pHQspi, uint8_t *pData)
{
    if( !pHQspi || !pData )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
        return HAL_ERROR;
    }

    if(pHQspi->State != HAL_QSPI_STATE_READY)
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
    pHQspi->State     = HAL_QSPI_STATE_BUSY_INDIRECT_TX;

    /* Configure the counter and handle size */
    pHQspi->TxXferCount = 0U;
    pHQspi->TxXferSize  = READ_REG(pHQspi->Instance->DLR) + 1U;
    pHQspi->pTxBuffPtr  = pData;
    pHQspi->Cache       = 0x0ul;

    /* Clear interrupt */
    WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TE | QSPI_FLAG_TC);

    /* The CCR register is configured with the function of indirect write */
    MODIFY_REG(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);

    __HAL_UNLOCK(pHQspi);

    /* Enable TE FT TC */
    SET_BIT(pHQspi->Instance->CR, QSPI_IT_TE | QSPI_IT_FT | QSPI_IT_TC);

    return HAL_OK;
}


/**
 *  @brief  Receive an amount of data in non-blocking mode with interrupt.
 *              This function is used only in Indirect Read Mode
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] pData       Pointer to data buffer
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Receive_IT(QSPI_HandleTypeDef *pHQspi, uint8_t *pData)
{
    uint32_t    addr_reg = READ_REG(pHQspi->Instance->AR);

    if( !pHQspi || !pData )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
        return HAL_ERROR;
    }

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
    pHQspi->State     = HAL_QSPI_STATE_BUSY_INDIRECT_RX;

    /* Configure the counter and handle size */
    pHQspi->RxXferCount = 0U;
    pHQspi->RxXferSize  = READ_REG(pHQspi->Instance->DLR) + 1U;
    pHQspi->pRxBuffPtr  = pData;
    pHQspi->Cache       = 0xFFFFFFFFul;

    /* Clear TE TC */
    WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TE | QSPI_FLAG_TC);

    /* The CCR register is configured with the function of indirect read */
    MODIFY_REG(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_READ);

    /* Rewrite AR register */
    WRITE_REG(pHQspi->Instance->AR, addr_reg);

    __HAL_UNLOCK(pHQspi);

    /* Enable TE FT TC Interrupts */
    __HAL_QSPI_ENABLE_IT(pHQspi, QSPI_IT_TE | QSPI_IT_FT | QSPI_IT_TC);

    return HAL_OK;
}

#if defined(HAL_DMA_MODULE_ENABLED)
/**
 *  @brief  Send an amount of data in non-blocking mode with DMA.
 *              This function is used only in Indirect Write Mode
 *              and the length of received buffer MUST be 4-align.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] pData       Pointer to data buffer.
 *                          ps. This buffer address and length MUST be 4-bytes alignment
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Transmit_DMA(QSPI_HandleTypeDef *pHQspi, uint8_t *pData)
{
    HAL_StatusTypeDef               rval = HAL_OK;
    uint32_t                        NBytes = (READ_REG(pHQspi->Instance->DLR) + 1U);

    if( !pData || ((uint32_t)pData & 0x3) || (NBytes & 0x3) )
    {
        // transferred length (byte) MUST be 4-align bytes
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
        return HAL_ERROR;
    }

    if( !pHQspi || !pHQspi->pHDMA )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_NULL_POINTER;
        return HAL_ERROR;
    }

    if(pHQspi->State != HAL_QSPI_STATE_READY)
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    do {
        union {
            HAL_DMA_Channel_Cfg_TypeDef     chnnl_cfg;
            HAL_DMA_Descriptor_Cfg_TypeDef  desc_cfg;
        } u;

        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY_INDIRECT_TX;

        HAL_DMA_Channel_Stop(pHQspi->pHDMA, HAL_DMA_CHANNEL_QSPI_RXTX);

        /* Set FIFO threshold to 26 */
        MODIFY_REG(pHQspi->Instance->CR, QUADSPI_CR_FTHRES, (26 - 1U) << QUADSPI_CR_FTHRES_Pos);

        /**
         *  Configure DMA Channel
         */
        u.chnnl_cfg.cfg_value           = 0ul;
        u.chnnl_cfg.is_enable_interrupt = true;
        u.chnnl_cfg.is_peri_trigger     = true;

        rval = HAL_DMA_Set_Channel(pHQspi->pHDMA, HAL_DMA_CHANNEL_QSPI_RXTX, &u.chnnl_cfg);
        if( rval )  break;

        /**
         *  Configure DMA data descriptor
         */
        u.desc_cfg.mode       = HAL_DMA_Mode_STOP;
        u.desc_cfg.type       = DMA_DESCRIPTOR_TYPE_PRIMARY;
        u.desc_cfg.xfer_bytes = NBytes;     // Only support 4-bytes alignment
        u.desc_cfg.unit_size  = DMA_DATA_SIZE_WORD;
        u.desc_cfg.arbit_rate = DMA_ARBITRATION_RATE_1;
        u.desc_cfg.timeout    = -1;
        u.desc_cfg.user_data  = 0;

        u.desc_cfg.src_addr   = (void*)pData;
        u.desc_cfg.dst_addr   = (void*)&pHQspi->Instance->DR;
        u.desc_cfg.src_inc    = DMA_DATA_INC_WORD;
        u.desc_cfg.dst_inc    = DMA_DATA_INC_NONE;

        pHQspi->TxXferCount = NBytes;
        pHQspi->TxXferSize  = pHQspi->RxXferCount;
        pHQspi->pTxBuffPtr  = pData;

        // Clear interrupt flag
        WRITE_REG(pHQspi->Instance->FCR, (QSPI_FLAG_TE | QSPI_FLAG_TC | QSPI_FLAG_FT));

        /* Configure to indirect write mode */
        WRITE_REG_MASK(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);

        rval = _QSPI_DMA_Config(pHQspi->pHDMA, HAL_DMA_CHANNEL_QSPI_RXTX, &u.desc_cfg);
        if( rval )
        {
            pHQspi->ErrorCode |= HAL_QSPI_ERROR_DMA;
            break;
        }

        pHQspi->State = HAL_QSPI_STATE_READY;
    } while(0);

    __HAL_UNLOCK(pHQspi);

    if( rval == HAL_OK )
    {
        __HAL_QSPI_ENABLE_IT(pHQspi, QSPI_IT_TE);

        SET_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN);
    }

    return rval;
}


/**
 *  @brief  Receive an amount of data in non-blocking mode with DMA.
 *              This function is used only in Indirect Read Mode
 *              and the length of received buffer MUST be 4-align.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] pData       Pointer to data buffer.
 *                          ps. This buffer length MUST be 4-bytes alignment
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Receive_DMA(QSPI_HandleTypeDef *pHQspi, uint8_t *pData)
{
    HAL_StatusTypeDef   rval = HAL_OK;
    uint32_t            addr_reg = READ_REG(pHQspi->Instance->AR);
    uint32_t            NBytes = READ_REG(pHQspi->Instance->DLR) + 1U;

    if( !pData || ((uint32_t)pData & 0x3U) || (NBytes & 0x3U) )
    {
        // transferred length (byte) MUST be 4-align bytes
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
        return HAL_ERROR;
    }

    if( !pHQspi || !pHQspi->pHDMA )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_NULL_POINTER;
        return HAL_ERROR;
    }

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    do {
        union {
            HAL_DMA_Channel_Cfg_TypeDef     chnnl_cfg;
            HAL_DMA_Descriptor_Cfg_TypeDef  desc_cfg;
        } u;

        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY_INDIRECT_RX;

        /* Set FIFO threshold to 4 */
        MODIFY_REG(pHQspi->Instance->CR, QUADSPI_CR_FTHRES, (4 - 1U) << QUADSPI_CR_FTHRES_Pos);

        /**
         *  Configure DMA Channel
         */
        u.chnnl_cfg.cfg_value           = 0ul;
        u.chnnl_cfg.is_enable_interrupt = true;
        u.chnnl_cfg.is_peri_trigger     = true;

        rval = HAL_DMA_Set_Channel(pHQspi->pHDMA, HAL_DMA_CHANNEL_QSPI_RXTX, &u.chnnl_cfg);
        if( rval )  break;

        /**
         *  Configure DMA data descriptor
         */
        u.desc_cfg.mode       = HAL_DMA_Mode_STOP;
        u.desc_cfg.type       = DMA_DESCRIPTOR_TYPE_PRIMARY;
        u.desc_cfg.xfer_bytes = NBytes;     // Only support 4-bytes alignment
        u.desc_cfg.unit_size  = DMA_DATA_SIZE_WORD;
        u.desc_cfg.arbit_rate = DMA_ARBITRATION_RATE_1;
        u.desc_cfg.timeout    = -1;
        u.desc_cfg.user_data  = 0;

        u.desc_cfg.src_addr   = (void*)&pHQspi->Instance->DR;
        u.desc_cfg.dst_addr   = (void*)pData;
        u.desc_cfg.src_inc    = DMA_DATA_INC_NONE;
        u.desc_cfg.dst_inc    = DMA_DATA_INC_WORD;

        pHQspi->RxXferCount = NBytes;
        pHQspi->RxXferSize  = pHQspi->RxXferCount;
        pHQspi->pRxBuffPtr  = pData;

        /* Clear transfer error and complete interrupt */
        WRITE_REG(pHQspi->Instance->FCR, (QSPI_FLAG_TE | QSPI_FLAG_TC));

        /* Configure QSPI to indirect read mode */
        MODIFY_REG(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_READ);

        /* Start the transfer by re-writing the address in AR register */
        WRITE_REG(pHQspi->Instance->AR, addr_reg);

        rval = _QSPI_DMA_Config(pHQspi->pHDMA, HAL_DMA_CHANNEL_QSPI_RXTX, &u.desc_cfg);
        if( rval )
        {
            pHQspi->ErrorCode |= HAL_QSPI_ERROR_DMA;
            break;
        }

        pHQspi->State = HAL_QSPI_STATE_READY;

    } while(0);

    __HAL_UNLOCK(pHQspi);

    if( rval == HAL_OK )
    {
        __HAL_QSPI_ENABLE_IT(pHQspi, QSPI_IT_TE);

        SET_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN);
    }

    return rval;
}

#endif  /* HAL_DMA_MODULE_ENABLED */


/**
 *  @brief  Configure the QSPI Automatic Polling Mode in blocking mode.
 *              This function is used only in Automatic Polling Mode
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] cmd         Pointer to a command structure, @ref QSPI_CommandTypeDef
 *  @param [in] cfg         Pointer to a polling configuration structure, @ref QSPI_AutoPollingTypeDef
 *  @param [in] Timeout     Timeout duration (unit: msec)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_AutoPolling(QSPI_HandleTypeDef *pHQspi, QSPI_CommandTypeDef *cmd, QSPI_AutoPollingTypeDef *cfg, uint32_t Timeout)
{
    HAL_StatusTypeDef   rval = HAL_OK;

    assert_param(IS_HAL_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));
    assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
    assert_param(IS_HAL_QSPI_ADDRESS_MODE(cmd->AddressMode));
    assert_param(IS_HAL_QSPI_ADDRESS_SIZE(cmd->AddressSize));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
    assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));
    assert_param(IS_HAL_QSPI_DATA_MODE(cmd->DataMode));
    assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));
    assert_param(IS_QSPI_INTERVAL(cfg->Interval));
    assert_param(IS_QSPI_STATUS_BYTES_SIZE(cfg->StatusBytesSize));
    assert_param(IS_QSPI_MATCH_MODE(cfg->MatchMode));

    if( !pHQspi || !cmd || !cfg )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_NULL_POINTER;
        return HAL_ERROR;
    }

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    do {
        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY_AUTO_POLLING;

        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_BUSY, RESET, Timeout);
        if( rval != HAL_OK )    break;

        //set match value,mask value,interval value
        WRITE_REG(pHQspi->Instance->PSMAR, cfg->Match);
        WRITE_REG(pHQspi->Instance->PSMKR, cfg->Mask);
        WRITE_REG(pHQspi->Instance->PIR, cfg->Interval);

        // set match mode and automatic stop enabled
        MODIFY_REG(pHQspi->Instance->CR, (QUADSPI_CR_PMM | QUADSPI_CR_APMS),
                   (cfg->MatchMode | HAL_QSPI_AUTOMATIC_STOP_ENABLE));

        // set  configuration parameters
        cmd->NbData = cfg->StatusBytesSize;
        _QSPI_Config(pHQspi, cmd, QSPI_FUNCTIONAL_MODE_AUTO_POLLING);

        // Wait for the SM flag position bit
        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_SM, SET, Timeout);
        if( rval )  break;

        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_SM);

        pHQspi->State = HAL_QSPI_STATE_READY;

    } while(0);

    __HAL_UNLOCK(pHQspi);

    return rval;
}

/**
 *  @brief  Configure the QSPI Automatic Polling Mode in non-blocking mode.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @param [in] cmd         Pointer to a command structure, @ref QSPI_CommandTypeDef
 *  @param [in] cfg         Pointer to a polling configuration structure, @ref QSPI_AutoPollingTypeDef
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_AutoPolling_IT(QSPI_HandleTypeDef *pHQspi, QSPI_CommandTypeDef *cmd, QSPI_AutoPollingTypeDef *cfg)
{
    HAL_StatusTypeDef           rval = HAL_OK;

    /* Check the parameters */
    assert_param(IS_HAL_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));
    assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
    assert_param(IS_HAL_QSPI_ADDRESS_MODE(cmd->AddressMode));
    assert_param(IS_HAL_QSPI_ADDRESS_SIZE(cmd->AddressSize));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
    assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));
    assert_param(IS_HAL_QSPI_DATA_MODE(cmd->DataMode));
    assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));
    assert_param(IS_QSPI_INTERVAL(cfg->Interval));
    assert_param(IS_QSPI_STATUS_BYTES_SIZE(cfg->StatusBytesSize));
    assert_param(IS_QSPI_MATCH_MODE(cfg->MatchMode));
    assert_param(IS_QSPI_AUTOMATIC_STOP(cfg->AutomaticStop));

    if( !pHQspi || !cmd || !cfg )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_NULL_POINTER;
        return HAL_ERROR;
    }

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHQspi);

    do {
        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY_AUTO_POLLING;

        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_BUSY, RESET, pHQspi->Timeout);
        if( rval )  break;

        //set match value,mask value,interval value
        WRITE_REG(pHQspi->Instance->PSMAR, cfg->Match);
        WRITE_REG(pHQspi->Instance->PSMKR, cfg->Mask);
        WRITE_REG(pHQspi->Instance->PIR, cfg->Interval);

        //Set the matching mode and automatic stop mode
        MODIFY_REG(pHQspi->Instance->CR, (QUADSPI_CR_PMM | QUADSPI_CR_APMS),
                   (cfg->MatchMode | cfg->AutomaticStop));

        // Clear TE SM interrupt
        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TE | QSPI_FLAG_SM);

        // set configuration parameters
        cmd->NbData = cfg->StatusBytesSize;
        _QSPI_Config(pHQspi, cmd, QSPI_FUNCTIONAL_MODE_AUTO_POLLING);

    } while(0);

    __HAL_UNLOCK(pHQspi);

    // Enable SM TE Interrupt
    SET_BIT(pHQspi->Instance->CR, (QSPI_IT_SM | QSPI_IT_TE));

    return rval;
}

/**
 * @brief  Configure the Memory Mapped mode.
 * @param  pHQspi : QSPI handle
 * @param  cmd : structure that contains the command configuration information.
 * @param  cfg : structure that contains the memory mapped configuration information.
 * @note   This function is used only in Memory mapped Mode
 * @retval HAL status
 */
HAL_StatusTypeDef HAL_QSPI_MemoryMapped(QSPI_HandleTypeDef *pHQspi, QSPI_CommandTypeDef *cmd, QSPI_MemoryMappedTypeDef *cfg)
{
    HAL_StatusTypeDef       rval = HAL_OK;

    assert_param(IS_HAL_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));
    assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
    assert_param(IS_HAL_QSPI_ADDRESS_MODE(cmd->AddressMode));
    assert_param(IS_HAL_QSPI_ADDRESS_SIZE(cmd->AddressSize));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));
    assert_param(IS_HAL_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
    assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));
    assert_param(IS_HAL_QSPI_DATA_MODE(cmd->DataMode));
    assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));
    assert_param(IS_QSPI_TIMEOUT_ACTIVATION(cfg->TimeOutActivation));
    assert_param(IS_QSPI_TIMEOUT_PERIOD(cfg->TimeOutPeriod));

    if( !pHQspi || !cmd || !cfg )
    {
        pHQspi->ErrorCode |= HAL_QSPI_ERROR_NULL_POINTER;
        return HAL_ERROR;
    }

    if( pHQspi->State != HAL_QSPI_STATE_READY )
        return HAL_BUSY;

    /* Process locked */
    __HAL_LOCK(pHQspi);

    do {
        pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
        pHQspi->State     = HAL_QSPI_STATE_BUSY_MEM_MAPPED;

        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_BUSY, RESET, pHQspi->Timeout);
        if( rval != HAL_OK )    break;

        // enable timeout counter
        MODIFY_REG(pHQspi->Instance->CR, QUADSPI_CR_TCEN, cfg->TimeOutActivation);

        if( cfg->TimeOutActivation == HAL_QSPI_TIMEOUT_COUNTER_ENABLE )
        {
            //Set the timeout value
            WRITE_REG(pHQspi->Instance->LPTR, cfg->TimeOutPeriod);

            // Clear TO flag interrupt
            WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TO);

            //enable TO interrupt
            SET_BIT(pHQspi->Instance->CR, QSPI_IT_TO);
        }

        // set configuration parameters
        _QSPI_Config(pHQspi, cmd, QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED);
    } while(0);

    __HAL_UNLOCK(pHQspi);

    return rval;
}



/**
 * @}
 */


/**
 *  @brief  Return the QSPI handle state.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      HAL state
 */
HAL_QSPI_StateTypeDef HAL_QSPI_GetState(QSPI_HandleTypeDef *pHQspi)
{
    return pHQspi->State;
}

/**
 *  @brief  Return the QSPI error code.
 *
 *  @param [in] pHQspi      Pointer to a QSPI handle
 *  @return
 *      error code of QSPI handle
 */
uint32_t HAL_QSPI_GetError(QSPI_HandleTypeDef *pHQspi)
{
    return pHQspi->ErrorCode;
}

/**
 *  \brief  Clear error code of QSPI handle
 *
 *  \param [in] pHQspi      Pointer to the QSPI Handle.
 *  \return
 *      None
 */
void HAL_QSPI_ClearError(QSPI_HandleTypeDef *pHQspi)
{
    pHQspi->ErrorCode = HAL_QSPI_ERROR_NONE;
    return;
}

/**
 *  @brief  Abort the current transmission.
 *
 *  @param [in] pHQspi      Pointer to the QSPI Handle.
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Abort(QSPI_HandleTypeDef *pHQspi)
{
    HAL_StatusTypeDef   rval = HAL_OK;

    // Check the QSPI is busy or not
    if( ((uint32_t)pHQspi->State & HAL_QSPI_STATE_BUSY) == 0U )
        return HAL_OK;

    __HAL_UNLOCK(pHQspi);

    do {
        if( READ_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN) )
        {
            // disable qspi dma
            CLEAR_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN);

        #if defined(HAL_DMA_MODULE_ENABLED)
            // stop DMA
            rval = HAL_DMA_Channel_Stop(pHQspi->pHDMA, HAL_DMA_CHANNEL_QSPI_RXTX);
            if( rval != HAL_OK )
                pHQspi->ErrorCode |= HAL_QSPI_ERROR_DMA;
        #endif  /* HAL_DMA_MODULE_ENABLED */
        }

        // abort qspi
        SET_BIT(pHQspi->Instance->CR, QUADSPI_CR_ABORT);

        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_TC, SET, pHQspi->Timeout);
        if( rval )      break;

        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TC);

        rval = _QSPI_WaitFlagUntilTimeout(pHQspi, QSPI_FLAG_BUSY, RESET, pHQspi->Timeout);
        if( rval )      break;

        // reset fmode
        CLEAR_BIT(pHQspi->Instance->CCR, QUADSPI_CCR_FMODE);

        pHQspi->State = HAL_QSPI_STATE_READY;
    } while(0);

    return rval;
}


/**
 *  @brief  Abort the current transmission (non-blocking function)
 *
 *  @param [in] pHQspi      Pointer to the QSPI Handle.
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_Abort_IT(QSPI_HandleTypeDef *pHQspi)
{
    HAL_StatusTypeDef   rval = HAL_OK;

    // Check the QSPI is busy or not
    if( ((uint32_t)pHQspi->State & HAL_QSPI_STATE_BUSY) == 0U )
        return HAL_OK;

    __HAL_UNLOCK(pHQspi);

    pHQspi->State = HAL_QSPI_STATE_ABORT;

    // Disable all interrupts
    CLEAR_BIT(pHQspi->Instance->CR, (QSPI_IT_TO | QSPI_IT_SM | QSPI_IT_FT | QSPI_IT_TC | QSPI_IT_TE));

    if( READ_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN) )
    {
        // disable qspi dma
        CLEAR_BIT(pHQspi->Instance->CR, QUADSPI_CR_DMAEN);

    #if defined(HAL_DMA_MODULE_ENABLED)
        if( HAL_DMA_Channel_Stop(pHQspi->pHDMA, HAL_DMA_CHANNEL_QSPI_RXTX) != HAL_OK )
        {
            pHQspi->State = HAL_QSPI_STATE_READY;

            HAL_QSPI_AbortCpltCallback(pHQspi);
        }
    #endif  /* HAL_DMA_MODULE_ENABLED */
    }
    else
    {
        // clear TC flag
        WRITE_REG(pHQspi->Instance->FCR, QSPI_FLAG_TC);

        // enable transfer complete interrupt and abort qspi
        SET_BIT(pHQspi->Instance->CR, QSPI_IT_TC | QUADSPI_CR_ABORT);
    }

    return rval;
}

/**
 *  @brief  Set QSPI timeout.
 *
 *  @param [in] pHQspi          Pointer to a QSPI handle
 *  @param [in] Timeout         The target timeout value (unit: msec)
 *  @return
 *      None
 */
void HAL_QSPI_SetTimeout(QSPI_HandleTypeDef *pHQspi, uint32_t Timeout)
{
    pHQspi->Timeout = Timeout;
    return;
}


/**
 *  @brief  Set FIFO threshold of QSPI master
 *
 *  @param [in] pHQspi          Pointer to a QSPI handle
 *  @param [in] Threshold       The target threshold of FIFO (between 1 ~ 16)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_SetFifoThreshold(QSPI_HandleTypeDef *pHQspi, uint32_t Threshold)
{
    HAL_StatusTypeDef   rval = HAL_OK;

    __HAL_LOCK(pHQspi);

    do {
        if( pHQspi->State != HAL_QSPI_STATE_READY )
        {
            rval = HAL_BUSY;
            break;
        }

        // init structure with new FIFO threshold value
        pHQspi->Init.FifoThreshold = Threshold;

        //Configure FIFO Threshold
        MODIFY_REG(pHQspi->Instance->CR, QUADSPI_CR_FTHRES, (Threshold - 1U) << QUADSPI_CR_FTHRES_Pos);
    } while(0);

    __HAL_UNLOCK(pHQspi);

    return rval;
}


/**
 *  @brief  Get the FIFO threshold of QSPI master
 *
 *  @param [in] pHQspi          Pointer to a QSPI handle
 *  @return
 *      The FIFO threshold of QSPI master
 */
uint32_t HAL_QSPI_GetFifoThreshold(QSPI_HandleTypeDef * pHQspi)
{
    return ((READ_BIT(pHQspi->Instance->CR, QUADSPI_CR_FTHRES) >> QUADSPI_CR_FTHRES_Pos) + 1U);
}

/**
 *  \brief  Set Flash Bank ID.
 *
 *  \param [in] pHQspi      Pointer to a QSPI handle.
 *  \param [in] BankID      Index of the flash bank memory to be accessed. @ref HAL_QSPI_FlashBKTypeDef.
 *                          ps. The BankID is ignored when dual flash mode is enabled.
 *  \return
 *      HAL status
 */
HAL_StatusTypeDef HAL_QSPI_SetFlashBankID(QSPI_HandleTypeDef *pHQspi, HAL_QSPI_FlashBKTypeDef BankID)
{
    HAL_StatusTypeDef   rval = HAL_OK;

    assert_param(IS_QSPI_FLASH_ID(BankID));

    __HAL_LOCK(pHQspi);

    do {
        if( pHQspi->State != HAL_QSPI_STATE_READY )
        {
            rval = HAL_BUSY;
            break;
        }

        // init structure with new Flash bank ID value
        pHQspi->Init.FlashBKID = BankID;

        // Configure QSPI Flash bank ID
        MODIFY_REG(pHQspi->Instance->CR, QUADSPI_CR_FSEL, BankID);
    } while(0);

    __HAL_UNLOCK(pHQspi);

    return rval;
}

/**
 * @}
 */



#endif /* HAL_QSPI_MODULE_ENABLED */

